// 声明一个桶 用来存储副作用函数
const bucket = new WeakMap()
// 定一个队列
const jobQueue = new Set()
// 拦截ownKeys时使用的作为追踪的key
const ITERATE_KEY = Symbol()
// 使用Promise.resolve（）创建一个promise实例，用它将一个任务添加进微任务队列
const p = Promise.resolve()
// 标志位代表是否正在刷新队列
let isFlushing = false
// 当前执行的副作用函数
let activeEffect

// 副作用函数栈
const effectStack = []
// 触发的操作类型
const TriggerType = {
  SET: 'SET',
  ADD: 'ADD',
  DELETE: 'DELETE'
}
// 副作用函数
function effect(fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }
  // 将options挂载到effectFn上
  effectFn.options = options
  effectFn.deps = []
  // 只有非lazy的情况下才会执行
  if (!options.lazy) {
    effectFn()
  }
  // 将副作用函数作为返回值返回
  return effectFn
}
// 计算属性
function computed(getter) {
  // value用来缓存上一次计算的值
  let value
  // dirty标志，用来标识是否需要重新计算值，为true则意味着脏，需要计算
  let dirty = true
  const effectFn = effect(getter, {
    lazy: true,
    // 添加调度器，在调度器中将dirty重置为true
    scheduler() {
      if (!dirty) {
        dirty = true
        // 当计算属性依赖的响应式数据变化时，手动调用trigger函数触发响应
        trigger(obj, 'value')
      }
    }
  })
  const obj = {
    get value() {
      if (dirty) {
        value = effectFn()
        dirty = false
      }
      // 读取value时，手动调用track进行追踪
      track(obj, 'value')
      return value
    }
  }
  return obj
}
// 侦听器
function watch(source, cb, options = {}) {
  let getter
  if (typeof source === 'function') {
    getter = source
  } else {
    getter = () => traverse(source)
  }
  // 定义旧值与新值
  let oldValue, newValue
  // 用来存储用户注册的过期回调
  let cleanup
  // 定义onInvalidate函数
  function onInvalidate(fn) {
    cleanup = fn
  }
  const job = () => {
    newValue = effectFn()
    // 在调用回调函数之前，先调用过期回调
    if (cleanup) {
      cleanup()
    }
    // 将onInvalidate作为回到函数的第三个参数，供用户使用
    cb(newValue, oldValue, onInvalidate)
    oldValue = newValue
  }
  const effectFn = effect(() => getter(), {
    lazy: true,
    scheduler: () => {
      if (options.flush === 'post') {
        const p = Promise.resolve()
        p.then(job)
      } else {
        job()
      }
    }
  })
  if (options.immediate) {
    job()
  } else {
    // 手动调用副作用函数，拿到的就是旧值
    oldValue = effectFn()
  }
}
// 侦听器工具函数 用来深度监听
function traverse(value, seen = new Set()) {
  // 如果要读取的数据是原始值，或者已经被读取过了，那么什么都不做
  if (typeof value !== 'object' || value === null || seen.has(value)) return
  // 将数据添加到seen中，代表遍历地读取过了，避免循环引用引起的死循环
  seen.add(value)
  // 假设value就是一个对象，使用for...in读取对象的每一个值，并递归地调用traverse进行处理
  for (const k in value) {
    traverse(value[k], seen)
  }
  return value
}
// 清除依赖函数
function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

// 依赖收集
function track(target, key) {
  if (!activeEffect) return
  let depsMap = bucket.get(target)
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()))
  }
  let deps = depsMap.get(key)
  if (!deps) {
    depsMap.set(key, (deps = new Set()))
  }
  deps.add(activeEffect)
  // deps就是一个与当前副作用函数存在联系的依赖集合
  // 将其添加到activeEffect.deps数组中 完成依赖收集
  activeEffect.deps.push(deps)
}

// 触发副作用函数
function trigger(target, key, type) {
  // 从桶里拿出来与target相关的依赖集合
  const depsMap = bucket.get(target)
  if (!depsMap) return
  // 找到与当前key相关的副作用函数们
  const effects = depsMap.get(key)
  // 声明一个Set结构的集合
  const effectsToRun = new Set()
  // 将与key相关的副作用函数们添加进effectsToRun
  effects &&
    effects.forEach((effectFn) => {
      if (effectFn !== activeEffect) {
        effectsToRun.add(effectFn)
      }
    })
  if (type === TriggerType.ADD || type === 'DELETE') {
    const iterateEffects = depsMap.get(ITERATE_KEY)
    // 将与ITERATE_KEY相关的副作用函数们添加进effectsToRun
    iterateEffects &&
      iterateEffects.forEach((effectFn) => {
        if (effectFn !== activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
  }

  effectsToRun.forEach((effectFn) => {
    // 如果一个副作用函数有调度器，那么优先调用该调度器，并将副作用函数作为参数传递
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      // 否则直接执行副作用函数，默认行为
      effectFn()
    }
  })
}

// 刷新任务队列
function flushJob() {
  // 如果当前处于刷新队列状态，则什么都不做
  if (isFlushing) {
    return
  }
  // 设置为true 标明正在处于刷新队列状态
  isFlushing = true
  // 在微任务队列中刷新队列
  p.then(() => {
    jobQueue.forEach((job) => job())
  }).finally(() => {
    // 结束后重置isFlushing
    isFlushing = false
  })
}



// 将一个普通对象转为响应式数据
function reactive(obj) {
  return new Proxy(obj, {
    // 拦截属性访问 obj.name
    get(target, key, receiver) {
      // 代理对象可以通过raw属性访问原始数据
      if (key === 'raw') {
        return target
      }
      track(target, key)
      return Reflect.get(target, key, receiver)
    },
    // 拦截in操作符 'name' in p
    has(target, key) {
      track(target, key)
      return Reflect.has(target, key)
    },
    ownKeys(target) {
      track(target, ITERATE_KEY)
      return Reflect.ownKeys(target)
    },
    set(target, key, newVal, receiver) {
      // 先获取旧值
      const oldVal = target[key]
      // type看看操作类型是添加新属性还是设置已有属性
      const type = Object.prototype.hasOwnProperty.call(target, key)
        ? 'SET'
        : 'ADD'
      // 拿到执行结果
      const res = Reflect.set(target, key, newVal, receiver)
      // 如果两者相等，说明receiver是target的代理对象
      if (target === receiver.raw) {
        if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
          trigger(target, key, type)
        }
      }
      return res
    },
    deleteProperty(target, key) {
      // 检查被操作的属性是否是对象自身的属性
      const hadKey = Object.prototype.hasOwnProperty.call(target, key)
      // 使用Reflect.deleteProperty完成属性的删除
      const res = Reflect.deleteProperty(target, key)

      if (res && hadKey) {
        // 只有当被删除的属性是自身属性且成功被删除时，才触发更新
        trigger(target, key, 'DELETE')
      }
      // 返回结果
      return res
    }
  })
}

const obj = { foo: 1 }
const proto = { bar: 1 }
const child = reactive(obj)
const parent = reactive(proto)
Object.setPrototypeOf(child, parent)

effect(() => {
  console.log(child.bar)
})
child.bar = 2
